﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Happy.Domain;
using Happy.Activities.Domain.Workflows;

namespace Happy.Activities.Domain.WorkflowInstances
{
    /// <summary>
    /// 工作流实例。
    /// </summary>
    public class WorkflowInstance : AggregateRoot<Guid>
    {
        /// <summary>
        /// 构造方法。
        /// </summary>
        public WorkflowInstance(Workflow workflow)
            : this()
        {
            this.State = WorkflowState.New;
            this.Workflow = workflow;
            this.WorkflowId = workflow.Id;
        }

        /// <summary>
        /// ORM专用。
        /// </summary>
        protected WorkflowInstance()
        {
            this.ActivityInstances = new List<ActivityInstance>();
            this.RouterInstances = new List<RouterInstance>();
        }

        /// <summary>
        /// 活动实例集合。
        /// </summary>
        protected internal virtual ICollection<ActivityInstance> ActivityInstances { get; set; }

        /// <summary>
        /// 返回活动实例数组。
        /// </summary>
        public ActivityInstance[] GetActivityInstances()
        {
            return this.ActivityInstances.ToArray();
        }

        /// <summary>
        /// 路由实例集合。
        /// </summary>
        protected internal virtual ICollection<RouterInstance> RouterInstances { get; set; }

        /// <summary>
        /// 返回活动实例数组。
        /// </summary>
        public RouterInstance[] GetRouterInstances()
        {
            return this.RouterInstances.ToArray();
        }

        /// <summary>
        /// 工作流状态。
        /// </summary>
        public WorkflowState State { get; protected set; }

        /// <summary>
        /// 所在流程标识。
        /// </summary>
        public virtual Guid WorkflowId { get; protected set; }

        /// <summary>
        /// 所在流程标识。
        /// </summary>
        public virtual Workflow Workflow { get; protected set; }

        /// <summary>
        /// 当正在进入活动时执行。
        /// </summary>
        public Action<WorkflowContext, ActivityInstance> OnActivityEntering { get; set; }

        /// <summary>
        /// 当已进入活动时执行。
        /// </summary>
        public Action<WorkflowContext, ActivityInstance> OnActivityEntered { get; set; }

        /// <summary>
        /// 当正在离开活动时执行。
        /// </summary>
        public Action<WorkflowContext, ActivityInstance> OnActivityExiting { get; set; }

        /// <summary>
        /// 当已离开活动时执行。
        /// </summary>
        public Action<WorkflowContext, ActivityInstance> OnActivityExited { get; set; }

        /// <summary>
        /// 当流程运行时执行。
        /// </summary>
        public Action<WorkflowContext> OnRunning { get; set; }

        /// <summary>
        /// 当流程结束时执行。
        /// </summary>
        public Action<WorkflowContext> OnCompleted { get; set; }

        /// <summary>
        /// 执行流程。
        /// </summary>
        public void Run(IDictionary<string, object> args)
        {
            var context = new WorkflowContext(this, args);

            this.Workflow.Run(context);
        }

        /// <summary>
        /// 唤醒流程。
        /// </summary>
        public void Resume(Guid activityInstanceId, IDictionary<string, object> args)
        {
            var context = new WorkflowContext(this, args);

            var activityInstance = this.ActivityInstances.First(x => x.Id == activityInstanceId);
            var activity = this.Workflow.Activities.First(x => x.Id == activityInstance.ActivityId);

            activity.Exit(context, activityInstance);
        }

        internal void AddActivityInstance(ActivityInstance activityInstance)
        {
            this.ActivityInstances.Add(activityInstance);
        }

        internal void AddRouterInstance(RouterInstance routerInstance)
        {
            this.RouterInstances.Add(routerInstance);
        }

        internal void SetStateToRunning(WorkflowContext context)
        {
            this.State = WorkflowState.Running;

            if (this.OnRunning != null)
            {
                this.OnRunning(context);
            }
        }

        internal void SetStateToCompleted(WorkflowContext context)
        {
            this.State = WorkflowState.Completed;

            if (this.OnCompleted != null)
            {
                this.OnCompleted(context);
            }
        }
    }
}
